Chapter 1. The eCos NAND Flash Chip Library

This section is solely of interest to those who want to implement a NAND Flash Chip device driver. Application programs will use only the public ANC API.

The chip device driver consists of two parts, common code and chip-specific code.

The common code performs:

  1. ONFI operations like interrogation and bad-block management;

  2. conventional chip interrogation; many of the chips on the market follow a common device identifier coding, and many of them mark bad blocks by setting the first byte of the spare area of the first page to zero; chips whose device identifier is listed in the table of known chip types cyg_nand_chip_id[] in packages/io/flash_nand/current/src/io_nand_chip.c, and that adhere to the factory-bad marking convention will be called 'regular' below;

  3. BBT (Bad Block Table) management, in a manner compatible with Linux MTD and u-boot.

The chip-specific component must provide the following code:

  1. in case of an ONFI chip: nothing, apart from allocating a device driver struct with the type set to ONFI;

  2. in case of a regular non-ONFI chip: nothing, apart from allocating a device driver struct with the type set to REGULAR;

  3. otherwise: code to correctly interpret the Read ID command and to query/set factory-defined bad-block markers.

Chip Device API

The NAND Flash Chip API is specified in cyg/io/flash_nand_chip.h.

If a chip is ONFI-conformant or regular, then the generic code in the common NAND Flash chip module can be used; in this case, there is no need for chip-specific code in the chip device driver.

The information on a chip that is used by the NAND Flash controller is recorded in the chip device's info structure (which reflects ONFI's Read Parameter response):

typedef enum CYG_NAND_FEATURES_FLAGS {
    CYGNUM_NAND_FEATURES_FLAGS_x16                 = (0x1 << 0),
    ...
} cyg_nand_flags_features_t;

typedef enum CYG_NAND_OPTIONAL_FLAGS {
    CYGNUM_NAND_OPTIONAL_FLAGS_PROGRAM_CACHE       = (0x1 << 0),
    CYGNUM_NAND_OPTIONAL_FLAGS_READ_CACHE          = (0x1 << 1),
    ...
    CYGNUM_NAND_OPTIONAL_FLAGS_COPYBACK            = (0x1 << 4),
    ...
} cyg_nand_flags_optional_t;

typedef struct CYG_NAND_CHIP_INFO {
    cyg_nand_flags_features_t   flags_features;
    cyg_nand_flags_optional_t   flags_optional;
    cyg_uint8                   manufacturer[12];       /**< filled with ' ' */
    cyg_uint8                   model[20];              /**< filled with ' ' */
    ...
    cyg_uint32                  data_bytes_per_page;
    cyg_uint16                  spare_bytes_per_page;
    cyg_uint32                  pages_per_block;
    cyg_uint32                  blocks_per_lun;
    cyg_uint8                   luns;
    ...
    cyg_uint8                   t_write_enable_pulse;   /**< in ns */
    cyg_uint8                   t_read_enable_pulse;    /**< in ns */
    ...
} cyg_nand_chip_info_t;

flags_features has the bit CYGNUM_NAND_FEATURES_FLAGS_x16 set if the device has x16 organisation; else, it has x8 organisation. flags_optional describes various optional capabilities of the chip. A lun (Logical UNit) may be a plane etc within the chip. As far as ONFI is concerned, a "chip" is something that has its own nCE (Chip Enable) line. If a physical chip has multiple NAND arrays that can separately be enabled by their own nCE line, the NAND arrays must be considered as separate chips, and each must have a separate NAND Flash Chip device driver. The controller's dispatch function chip_select() must be used for selection of the correct NAND array.

Because ONFI is recent, many large-page chips support most of the ONFI command set (e.g. page program command = 0x80), but they are often not *completely* compliant. Usually, the interrogation command is custom, as is the format for bad block marks. For interrogation, non-ONFI chips support a Read ID command, which returns 2 to 5 bytes of information. The first byte is always the manufacturer code, the second byte is always the chip model code. The meaning of bytes 3..5 (insofar as they are present) is usually standardized: byte 3 encodes the number of chips within the package, and indicates whether interleaved or cached access is supported; byte 4 encodes page data and spare size, block size, x8/x16 organisation, and minimum timing information; and byte 5 encodes the number of planes/LUNs, and data size per LUN.

The common part of the NAND Flash Chip library has an implementation for ONFI chips and an implementation for regular non-ONFI chips. A custom device driver is only necessary for non-regular, non-ONFI chips.

The NAND Flash Chip Device Structure

ONFI-compliant and regular chips have no need for device-specific code. Non-regular, non-ONFI chips must create a function dispatch table for their device driver.

/**
 * Function dispatch table that must be used for non-ONFI, non-regular
 * chips.
 */
typedef struct CYG_NAND_CHIP_FUNS {
    int       (*chip_init)(cyg_nand_ctl_t *ctl, cyg_nand_chip_t *chip);
                /** parse the 'Read ID' block info. */
    int       (*chip_parse_info)(cyg_nand_ctl_t *ctl,
                                 cyg_nand_chip_t *chip,
                                 const cyg_uint8 *block);
                /**
                 * Check for factory-marked bad blocks
                 * @param row: row address, includes (ignored) page
                 * @return 0 -> good; 1 -> bad; -errno : error */
    int       (*chip_block_is_bad)(const cyg_nand_chip_t *chip, size_t row);
} cyg_nand_chip_funs_t;

chip_init() performs any pre-interrogation initialization. Typically, the slowest communication mode is chosen. The controller then performs interrogation, and afterwards invokes chip_parse_info(). Any information that cannot be derived from interrogation must be filled in in chip_parse_info(), based on the datasheet. chip_block_is_bad() performs factory-bad-block query in the manner prescribed in the datasheet.

/**
 * Type of chip
 */
typedef enum CYG_NAND_CHIP_TYPE {
    CYG_NAND_CHIP_TYPE_ONFI,
    CYG_NAND_CHIP_TYPE_REGULAR,
    CYG_NAND_CHIP_TYPE_CUSTOM,
} cyg_nand_chip_type_t;

/**
 * Boolean capabilities of the NAND Flash chip
 */
typedef enum CYG_NAND_CHIP_FLAGS {
    CYG_NAND_CHIP_FLAGS_CMD_IS_SMALLPAGE        = (0x1 << 1),
    CYG_NAND_CHIP_FLAGS_DATA_x16                = (0x1 << 2),
    CYG_NAND_CHIP_FLAGS_NO_BBT                  = (0x1 << 3),
} cyg_nand_chip_flags_t;

/**
 * NAND Flash Chip device driver structure
 */
struct CYG_NAND_CHIP {
    const cyg_nand_chip_funs_t *funs;           /**< Non-ONFI dispatch table */
    cyg_uint32                  devno;          /**< My devno */
    cyg_uint32                  anc_devno;      /**< Owning ANC devno */
    cyg_uint32                  ctl_devno;      /**< Owning controller devno */
                /** If not ONFI, set a bit in the mask for each meaningful
                 * byte in the 'Read ID' response. */
    cyg_uint32                  bytes_mask;

    cyg_nand_chip_flags_t       flags;

    int                         initialized;
    cyg_nand_chip_info_t        info;
    cyg_nand_ctl_t             *ctl;            /**< My controller */
    cyg_nand_bbt_t              bbt;            /**< Bad Block Table */
    cyg_uint8                   spare_buffer[CYGNUM_NAND_SPARE_MAX];
};

If a chip is initialised with the bit CYG_NAND_CHIP_FLAGS_NO_BBT cleared in its cyg_nand_chip->flags field, the initialization routine looks for blocks that contain a previously created BBT (Bad Block Table) in some regions of the chip. If no BBT is found, the whole chip is scanned for factory-bad blocks using chip->chip_block_is_bad(), and the BBT is recorded on the chip. If bit CYG_NAND_CHIP_FLAGS_NO_BBT is set, the only bad-block management that is possible is by inspecting factory-bad blocks. This is not recommended policy.

#define CYG_NAND_DRIVER_CHIP(name, i_funs, i_devno, i_anc, i_ctl, i_type, i_flags,
i_bytes_mask)        \
cyg_nand_chip_t name CYG_HAL_TABLE_ENTRY(cyg_nand_dev_chip) = {         \
    .funs        = i_funs,                                              \
    .devno       = i_devno,                                             \
    .anc_devno   = i_anc,                                               \
    .ctl_devno   = i_ctl,                                               \
    .type        = i_type,                                              \
    .flags       = i_flags,                                             \
    .bytes_mask  = i_bytes_mask,                                        \
}

A NAND Flash chip device structure is instantiated with macro CYG_NAND_DRIVER_CHIP(). The values for devno, anc_devno, ctl_devno, type, and flags are usually specified in the target CDL. ONFI chips must set the type field to CYG_NAND_CHIP_TYPE_ONFI, and funs can be NULL. Regular chips must set the type to CYG_NAND_CHIP_TYPE_REGULAR, and funs can be NULL. Otherwise, funs must point to a struct that contains the dispatch table for this type of chip, and bytes_mask describes how much Read ID information is present: it must have set (0x1 << bytenum) for each meaningful byte bytenum.